package net.guruqu.cds;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;
import java.util.Vector;


public class GraphVisualizer extends Canvas implements MouseWheelListener,
		MouseMotionListener, MouseListener, KeyListener {
	public boolean active;
	public int rounds;
	private class GNode {
		public int UID;
		public Vector2f position;
	}
public boolean pause=false;
	Vector<GNode> nodes;
	Network network;
	HashMap<Integer, HashSet<Integer>> edges;
	float preferedSpan = 100;

	public GraphVisualizer() {
		edges = new HashMap<Integer, HashSet<Integer>>();
		nodes = new Vector<GNode>();
		active = true;
		addMouseWheelListener(this);
		addMouseMotionListener(this);
		addMouseListener(this);
		addKeyListener(this);
	}

	public void setGraph(int nodes, Vector<int[]> edges) {
		for (int i = 0; i < nodes; i++) {
			GNode nn = new GNode();
			this.nodes.add(nn);
			nn.position = new Vector2f();
			nn.UID = i;

			HashSet<Integer> hs = new HashSet<Integer>();
			this.edges.put(i, hs);
		}

		for (int[] i : edges) {
			this.edges.get(i[0]).add(i[1]);
			this.edges.get(i[1]).add(i[0]);
		}
	}

	static Random random = new Random(513);

	public void initialize() {
		for (GNode n : nodes) {
			n.position.set((int) (getWidth() * random.nextFloat()),
					(int) (getHeight() * random.nextFloat()));
		}
	}

	public void updateStat() {
		if(pause)
			return;
		float ALPHA = -0.01f;
		for (int i = 0; i < nodes.size(); i++) {
			GNode na = nodes.get(i);
			Vector2f delta = new Vector2f();
			for (int j : edges.get(i)) {
				Vector2f v2f = nodes.get(j).position.subtract(na.position);
				float len = v2f.length();
				v2f.normalizeLocal();
				v2f.multLocal(ALPHA * (preferedSpan - len));
				delta.addLocal(v2f);
			}
			delta.divideLocal(edges.get(i).size());

			if (na != dragging)
				na.position.addLocal(delta);

		}
	}

	BufferedImage offScr = null;
	int NODE_SIZE = 15;

	@Override
	public void paint(Graphics g) {
		updateStat();

		if (offScr == null || offScr.getWidth() != getWidth()
				|| offScr.getHeight() != getHeight()) {
			offScr = new BufferedImage(getWidth(), getHeight(),
					BufferedImage.TYPE_INT_ARGB);
		}
		Graphics2D g2d = offScr.createGraphics();
		if (active)
			g2d.setColor(Color.white);
		else
			g2d.setColor(new Color(0.9f, 0.8f, 0.85f, 1f));
		g2d.fillRect(0, 0, getWidth(), getHeight());
		g2d.setColor(new Color(1, 0, 0, 0.8f));
		for (int i : edges.keySet()) {
			HashSet<Integer> edge = edges.get(i);
			for (int j : edge) {
				if (i > j) {
					if (!active) {
						g2d.setColor(new Color(1, 0, 0, 0.05f));
					}
					g2d.drawLine((int) nodes.get(i).position.x, (int) nodes
							.get(i).position.y, (int) nodes.get(j).position.x,
							(int) nodes.get(j).position.y);
				}
			}
		}

		for (GNode n : nodes) {
				g2d.setColor(new Color(0, 0, 0, 0.2f));

			g2d.fillOval((int) (n.position.x - NODE_SIZE / 2),
					(int) (n.position.y - NODE_SIZE / 2), NODE_SIZE, NODE_SIZE);
			g2d.setXORMode(Color.white);
			g2d.drawString("" +((Node) network.getNode(n.UID)).getText(), (int) n.position.x, (int) n.position.y);
			g2d.setPaintMode();
		}

		g2d.setColor(Color.black);
		g2d.drawString("Round:" + rounds, 10, 10);
		g.drawImage(offScr, 0, 0, this);

		repaint();
	}

	@Override
	public void update(Graphics g) {
		// TODO Auto-generated method stub
		paint(g);
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent evt) {
		// TODO Auto-generated method stub
		if (evt.getWheelRotation() > 0) {
			preferedSpan *= 1.3f;
			if (preferedSpan > 500)
				preferedSpan = 500;
		} else {
			preferedSpan /= 1.3f;
			if (preferedSpan < 50)
				preferedSpan = 50;
		}
	}

	GNode dragging = null;

	@Override
	public void mouseDragged(MouseEvent evt) {
		// TODO Auto-generated method stub
		if (dragging == null) {
			for (GNode n : nodes) {
				if (n.position.distance(new Vector2f(evt.getX(), evt.getY())) < NODE_SIZE / 2)
					dragging = n;
			}
		}
		if (dragging != null) {
			dragging.position.set(evt.getX(), evt.getY());
		}
	}

	@Override
	public void mouseMoved(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseClicked(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mousePressed(MouseEvent evt) {
		// TODO Auto-generated method stub
		mouseDragged(evt);
	}

	@Override
	public void mouseReleased(MouseEvent evt) {
		// TODO Auto-generated method stub
		dragging = null;
	}

	public Network getNetwork() {
		return network;
	}

	public void setNetwork(Network network) {
		this.network = network;
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub
		if(e.getKeyChar()==' ')
			pause=!pause;
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}
}
